#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# pidgin-bt 0.3
# (c) Jan Holthuis <holthuis.jan@googlemail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
import sys, os, bluetooth, dbus, obexftp, time, string, ConfigParser
from PyQt4 import Qt, QtCore, QtGui
class Device(object):
	def __init__(self,address):
		self.address = address

	def is_valid(self):
		if bluetooth.is_valid_address(self.address) is False:
			return False
		self.name = bluetooth.lookup_name(self.address)
		if self.name is None:
			return False
		else:
			return True
	def check_vnt(self):
		# Organizationally Unique Identifiers (OUI) from http://standards.ieee.org/regauth/oui/index.shtml
		# List contains all OUI-adresses belonging to Sony Ericsson Mobile Communications (AB)
		mac_list = ("00:0A:D9","00:0E:07","00:0F:DE","00:12:EE","00:16:20","00:16:B8","00:18:13","00:19:63","00:1A:75","00:1B:59","00:1C:A4","00:1D:28","00:1E:45","00:1E:DC","00:1F:E4","00:21:9E","00:22:98","00:23:45","00:23:F1","00:24:EF","00:25:E7","6C:0E:0D")
		if self.address[0:8] in mac_list:
			# Device is a Sony Ericsson phone
			# PS: I'm not sure if ALL phones by SE support VNT, but I hope so :-P
			return True
		else:
			# Device is NOT a Sony Ericsson phone
			return False
class Notification(object):
	def __init__(self, sender, message, msgtime):
		self.sender = sender
		self.message = message
		self.msgtime = msgtime
	def get_plain(self):
		text = "New message from "
		text += self.sender + "\n"
		text += time.strftime("%Y-%m-%d %H:%M:%S",self.msgtime) + "\n\n"
		text += self.message + "\n------------------------------\n"
		return (text,"BTMsg.txt")
	def get_vnt(self):
		msgtime_f = time.strftime("%Y%m%d",self.msgtime)+"T"+time.strftime("%H%M%S",self.msgtime)+"Z"
		text = "BEGIN:VNOTE\n"
		text += "VERSION:1.1\n"
		text += "BODY;ENCODING=QUOTED-PRINTABLE:"
		text += self.message.replace("\n","=0A")+"=0A--"+self.sender+"\n"
		text += "DCREATED:"+msgtime_f+"\n"
		#text += "LAST-MODIFIED:"+msgtime_f+"\n"
		text += "CLASS:PUBLIC\n"
		text += "X-IRMC-LUID:000001000000\n"
		text += "END:VNOTE"
		return (text,"note.vnt")
class PidginBt(object):
	def __init__(self):
		# Get configuration
		self.conf = ConfigParser.ConfigParser()
		self.conf.read([os.path.join(os.path.dirname(sys.argv[0]),"defaults.cfg"),"/etc/pidgin-bt.cfg",os.path.expanduser('~/.pidgin-bt.cfg')])
		self.dev = Device("")
		self.get_device_config()
		if __name__ == "__main__":
			app = QtGui.QApplication(sys.argv)

			menu = QtGui.QMenu("Pidgin-Bt")
			self.tray = QtGui.QSystemTrayIcon(QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),"pidgin-bt-offline.png")))

			self.enable = QtGui.QAction(QtGui.QIcon("phone.png"),"Enable",menu)
			self.enable.setCheckable(True)
			try:
				if self.conf.getboolean("main","enabled_by_default"):
					self.enable.setChecked(True)
					self.trigger_notifications()
				else:
					self.enable.setChecked(False)
			except ConfigParser.NoOptionError:
				# Not defined, fall back to default behaviour
				self.conf.set("main","enabled_by_default","no")
				self.enable.setChecked(False)
	        	menu.addAction(self.enable)
			self.enable.connect(self.enable, QtCore.SIGNAL("triggered()"), self.trigger_notifications)

			self.vnt = QtGui.QAction(QtGui.QIcon("vnt.png"),"Use VNT",menu)
			self.vnt.setCheckable(True)
			try:
				self.vnt.setChecked(self.conf.getboolean("main","use_vnt"))
			except ConfigParser.NoOptionError:
				# Not defined, fall back to default behaviour
				self.conf.set("main","use_vnt","no")
				self.vnt.setChecked(False)
	        	menu.addAction(self.vnt)
			self.vnt.connect(self.vnt, QtCore.SIGNAL("triggered()"), self.trigger_vnt)

			self.seldev = QtGui.QAction(QtGui.QIcon("configure.png"),"Select device",menu)
			self.seldev.setCheckable(False)
	        	menu.addAction(self.seldev)
			self.seldev.connect(self.seldev, QtCore.SIGNAL("triggered()"), self.set_device)
			
			self.test = QtGui.QAction(QtGui.QIcon("status.png"),"Test",menu)
			self.test.setCheckable(False)
	        	menu.addAction(self.test)
			self.test.connect(self.test, QtCore.SIGNAL("triggered()"), self.do_test)

			#searchAction = menu.addAction(QtGui.QIcon("view_refresh.png"),"Search Again")
			#searchAction.connect(searchAction, QtCore.SIGNAL("triggered()"), tester, QtCore.SLOT("start()"))
			quitAction = menu.addAction(QtGui.QIcon("application_exit.png"),"Quit")
			quitAction.connect(quitAction, QtCore.SIGNAL("triggered()"), app, QtCore.SLOT("quit()"))

			# Writing our configuration file
			with open(os.path.expanduser('~/.pidgin-bt.cfg'), 'wb') as configfile:
				self.conf.write(configfile)

			self.tray.setContextMenu(menu)
			self.tray.show()
			self.tray.setToolTip("Pidgin-Bluetooth-Notifier")

			from dbus.mainloop.qt import DBusQtMainLoop
			dbus.mainloop.qt.DBusQtMainLoop(set_as_default=True)
			self.bus = dbus.SessionBus()
			obj = self.bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
			self.pidginbus = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
			self.bus.add_signal_receiver(self.get_message,dbus_interface="im.pidgin.purple.PurpleInterface",signal_name="ReceivedImMsg")
			sys.exit(app.exec_())
	def select_device(self):
		self.tray.showMessage("Please wait","Scanning for nearby Bluetooth-devices...", Qt.QSystemTrayIcon.Information)
		print "Searching for nearby Bluetooth devices..."
		nearby_devices = bluetooth.discover_devices(lookup_names = True)
		if len(nearby_devices) is 0:
			# No devices Found
			self.tray.showMessage("Nothing found","Please make sure that bluetooth is enabled and the device is in discoverable mode", Qt.QSystemTrayIcon.Warning)
			print "No Devices found! :("
			return None
		device_list = QtCore.QStringList()
		device_dict = {}
		for mac, name in nearby_devices:
			device_list.append("%s" % name)
			device_dict[name] = mac
		selection = QtGui.QInputDialog.getItem(None,"Select Bluetooth-device","Please type your device's MAC-address:",device_list)
		print "Pidgin-Bt is now using %s (%s)" % (selection[0],device_dict[str(selection[0])])
		return device_dict[str(selection[0])]
	def is_active(self):
		if self.enable.isChecked():
			return True
		else:
			return False
	def get_device_config(self):
		valid = False
		while not valid:
			try:
				mac_addr = self.conf.get("main","device")
			except ConfigParser.NoOptionError:
				mac_addr = self.select_device()
			if not bluetooth.is_valid_address(mac_addr):
				mac_addr = self.select_device()
			valid = (mac_addr is not None and bluetooth.is_valid_address(mac_addr))
			if valid:
				self.conf.set("main","device",mac_addr)
				# Writing our configuration file
				with open(os.path.expanduser('~/.pidgin-bt.cfg'), 'wb') as configfile:
					self.conf.write(configfile)
				self.dev = Device(mac_addr)
	def set_device(self):
		mac_addr = self.select_device()
		if mac_addr is not None and bluetooth.is_valid_address(mac_addr):
			self.conf.set("main","device",mac_addr)
			# Writing our configuration file
			with open(os.path.expanduser('~/.pidgin-bt.cfg'), 'wb') as configfile:
				self.conf.write(configfile)
			self.dev = Device(mac_addr)
		else:
			print "No valid device chosen, config remains unchanged."
	def trigger_vnt(self):
		if self.vnt.isChecked():
			# enable VNT
			message = "Notifications will be sent in VNT format instead of plain text."
			if not self.dev.check_vnt():
				message += " (Are you sure, your device supports it? It seems not to be a Sony Ericsson phone!)"
			self.tray.showMessage("Sony VNT Notes", message, Qt.QSystemTrayIcon.Information)
			self.vnt.setChecked(True)
			self.conf.set("main","use_vnt","yes")
			print "Notification will be sent in Sony's VNT format."
		else:
			# disable VNT
			message = "Notifications will be sent in plain text."
			if self.dev.check_vnt():
				message += " (Please note, that this phone is likely to be capable of VNT, too!)"
			self.tray.showMessage("Plain Text Notes", message, Qt.QSystemTrayIcon.Information)
			self.vnt.setChecked(False)
			self.conf.set("main","use_vnt","no")
			print "Notification will be sent in plain text."
		# Writing our configuration file
		with open(os.path.expanduser('~/.pidgin-bt.cfg'), 'wb') as configfile:
			 self.conf.write(configfile)
	def trigger_notifications(self):
		if self.enable.isChecked():
			self.get_device_config()
			try:
				if self.dev.is_valid():
					self.tray.showMessage("Device found!", "Device \"%s\" will be used for notifications" % self.dev.name, Qt.QSystemTrayIcon.Information)
					self.tray.setIcon(QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),"pidgin-bt.png")))
					print "Notifications enabled!"
				else:
					self.tray.showMessage("Device not found!", "Device %s couldn't be found! Notifications will be disabled." % self.dev.address, Qt.QSystemTrayIcon.Critical)
					self.tray.setIcon(QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),"pidgin-bt-offline.png")))
					self.enable.setChecked(False)
					print "Notifications disabled! (invalid device)"
			except bluetooth.BluetoothError, errormsg:
				self.tray.showMessage("Bluetooth Error!", string.capitalize(str(errormsg)), Qt.QSystemTrayIcon.Critical)
				self.tray.setIcon(QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),"pidgin-bt-offline.png")))
				self.enable.setChecked(False)
				print "Notifications disabled! (Bluetooth error)"

		else:
			self.tray.setIcon(QtGui.QIcon(os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),"pidgin-bt-offline.png")))
			self.tray.showMessage("Notifications disabled", "You will no longer be notified if new messages are received.", Qt.QSystemTrayIcon.Warning)
			print "Notifications disabled!"
	def do_test(self):
		message = "This is a simple test message.\nIf you can read this, your Pidgin-Bluetooth-Notifier works."
		self.send_notification("PiginBt-Service",message,time.localtime())
		print "Test message sent."
	def get_message(self, account, sender, message, conversation, flags):
		if self.is_active():
			name = self.pidginbus.PurpleBuddyGetAlias(self.pidginbus.PurpleFindBuddy(account,str(sender.split("@")[0])))
			obj = self.bus.get_object("org.freedesktop.ScreenSaver", "/ScreenSaver")
			screen = dbus.Interface(obj, "org.freedesktop.ScreenSaver")
			if screen.GetActive():
				self.tray.showMessage("New Message!", "You received a new message via Pidgin! Sending it to \"%s\"..." % self.dev.name, Qt.QSystemTrayIcon.Information)
				self.send_notification(name, message, time.localtime())
				print "Sending new message to bluetooth device..."
	def send_notification(self, sender, message, msgtime):
		notification = Notification(sender, message, msgtime)
		if self.vnt.isChecked():
			# Sony VNT format
			self.send_text(notification.get_vnt())
		else:
			# plain text
			self.send_text(notification.get_plain())
	def send_text(self,arg):
		text = arg[0]
		filename = os.path.join("/tmp",arg[1])
		cli = obexftp.client(obexftp.BLUETOOTH)
		channel = obexftp.browsebt(self.dev.address, obexftp.PUSH)
		cli.connectpush(self.dev.address, channel)
		text_file = open(filename, "w")
		text_file.write(text)
		text_file.close()
		cli.put_file(filename)
		cli.disconnect()
		cli.delete(None)


pidgin = PidginBt()
